<template>
    <div></div>
</template>

<script setup>
import { onMounted } from "vue";
import * as THREE from 'three'
// 轨道控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import * as d3 from "d3";
import Base from "./Base"
import { flylineAddress, airplaneAddress, centerAddress } from "./constant";

let base, controls;
onMounted(async () => {
    base = new Base();
    // 将摄像机拉远一点
    base.camera.position.z = 60;
    // 更新投影矩阵
    base.camera.updateProjectionMatrix();
    // 添加光照
    base.addAmbientLight(0.55)
    controls = new OrbitControls(base.camera, base.renderer.domElement);
    // 加载地图
    await loadMap();
    update()
    window.addEventListener("resize", resize);
})

// 加载地图
async function loadMap() {
    const fileloader = new THREE.FileLoader();
    let res = await Promise.all([
        fileloader.loadAsync("/json/china.json"),
        fileloader.loadAsync("/json/chinaedeg.json")
    ])
    if(res instanceof Array) {
        // 创建地图
        createMap(res[0]);
    }
}
const projection = d3
    .geoMercator() // 地球投影方式（用于绘制球形墨卡托投影）
    .center([108.5525, 34.3227])  // 地图中心点经纬度坐标
    .scale(84) // 缩放
    .translate([0, 0]); // 移动地图位置
// 定义一个父类
let chinaObj = new THREE.Object3D();

/**
 * 创建地图
*/
function createMap(res) {
    res = JSON.parse(res);
    let centerCoord = null;
    res.features?.forEach(province => {
        let provinceObj = new THREE.Object3D();
        if(province.geometry.type === "MultiPolygon") {
            province.geometry.coordinates.forEach(multipolygon => {
                multipolygon.forEach(polygon => {
                    let shape = new THREE.Shape();
                    // 因为希望将省份之间的分割线画出来，所以定义一个数组用来接收坐标。
                    let arr = []; // 用来接收
                    polygon.forEach((coord, index) => {
                        // 转化为二维坐标
                        let [ x, y ] = projection(coord);
                        if(index === 0) {
                            // 注意y之前需要添加负号-，因为转换后的y是颠倒的 
                            shape.moveTo(x, -y);
                        } else {
                            shape.lineTo(x, -y);
                        }
                        // 将坐标添加到数组中，第三个参数为1:表示默认深度为1
                        arr.push(x, -y, 1);
                    })
                    // 创建多边形
                    let mesh = createPolygon(shape, arr, province)
                    provinceObj.add(mesh);
                })
            })
        } else if(province.geometry.type === "Polygon") {
            province.geometry.coordinates.forEach(polygon => {
                let shape = new THREE.Shape();
                // 因为希望将省份之间的分割线画出来，所以定义一个数组用来接收坐标。
                let arr = []; // 用来接收
                polygon.forEach((coord, index) => {
                    // 转化为二维坐标
                    let [ x, y ] = projection(coord);
                    if(index === 0) {
                        // 注意y之前需要添加负号-，因为转换后的y是颠倒的 
                        shape.moveTo(x, -y);
                    } else {
                        shape.lineTo(x, -y);
                    }
                    // 将坐标添加到数组中，第三个参数为1:表示默认深度为1
                    arr.push(x, -y, 1);
                })
                // 创建多边形
                let mesh = createPolygon(shape, arr, province)
                provinceObj.add(mesh);
            })
        }
        if(province.properties.name === centerAddress) {
            centerCoord = createSprite(province, "/sprites/光圈.png", true);
        }
        console.log("provinceObj=", provinceObj)
        chinaObj.add(provinceObj);
    })
    // 遍历添加图片
    if(centerCoord) {
        res.features?.forEach(province => {
            // 飞线
            if(flylineAddress.includes(province.properties.name)) {
                let address = createSprite(province,  "/sprites/circle.png", false);
                addressLines(centerCoord, address);
            }
            // 飞机
            if(airplaneAddress.includes(province.properties.name)) {
                let address = createSprite(province,  "/sprites/circle.png", false);
                createSprite(province,  "/sprites/圆圈.png", true, 3);
                addressLines(centerCoord, address);
            }
        })
    }
    // 将父类添加到场景中
    base.scene.add(chinaObj);
}

// 定义材质
const extrudeMats = [
    new THREE.MeshStandardMaterial({
        color: 0x0000ff,
        transparent: true,
        opacity: 0.85,
        blending: THREE.AdditiveBlending,
    }),
    new THREE.MeshStandardMaterial({
        color: 0x0000ff,
        transparent: true,
        opacity: 0.35,
        blending: THREE.AdditiveBlending,
    })
]
// 边缘的材质：定义一个基础的线段材质
const edgeMat = new THREE.LineBasicMaterial({
    color: 0xffffff,
    blending: THREE.AdditiveBlending,
})

/**
 * 创建多边形 
*/
function createPolygon(shape, arr, province) {
    // 创建挤压缓冲几何体
    let geo = new THREE.ExtrudeGeometry(shape);
    let mesh = new THREE.Mesh(geo, extrudeMats);
    if(province.properties.name) {
        mesh.name = province.properties.name
    }
    // 创建画线
    let buffer = new THREE.BufferGeometry();
    buffer.setAttribute(
        "position",
        new THREE.BufferAttribute(new Float32Array(arr), 3)
    )
    let line = new THREE.Line(buffer, edgeMat);
    // 将连线添加到父类中
    chinaObj.add(line);
    // mesh需要添加到provinceObj中
    return mesh
}

// 创建一个立方体（测试使用）
function createBox() {
    const geometry = new THREE.BoxGeometry( 1, 1, 1 );
    const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
    const cube = new THREE.Mesh( geometry, material );
    base.scene.add( cube );
}

const textureloader = new THREE.TextureLoader();
// 添加动态的圆点
const animCircle = []
/**
 * 创建精灵图片
 * @param province: 省份信息
 * @param imageurl: 图片路径
 * @param needanim: 是否需要动画
 * @param scaleparam: 缩放参数
 * @param opacityparam: 透明度参数
 * @param speed: 速度
*/
function createSprite(province, imageurl, needanim=false, scaleparam=2.5, opacityparam=10.0, speed=0.015) {
    // 省份中心点
    let [ x, y ] = projection(province.properties.center);
    const map = textureloader.load(imageurl);
    let meterial = new THREE.MeshBasicMaterial({
        map,
        transparent: true,
        depthWrite: false,
        depthTest: false,
        blending: THREE.AdditiveBlending
    });
    let sprite = new THREE.Sprite(meterial);
    if(needanim) {
        animCircle.push({
            circle: sprite,
            offset: 0,
            speed,
            scaleparam,
            opacityparam,
        })
    }
    sprite.position.set(x, -y, 1);
    chinaObj.add(sprite);
    return [x, -y, 1]
}

// 一条线上的段数
const PointsCount = 100;
// 曲线材质
const addressMat = new THREE.LineBasicMaterial({
    color: 0xff0000,
});

/**
 * 添加连线
 * @param start: 开始坐标
 * @param end: 结束坐标
*/
function addressLines(start, end) {
    // 创建一个三维二次贝塞尔曲线
    const curve = new THREE.QuadraticBezierCurve3(
        new THREE.Vector3(...start),
        new THREE.Vector3((start[0]+end[0])/2, (start[1]+end[1])/2, 9),
        new THREE.Vector3(...end),
    )
    const points = curve.getPoints(PointsCount);
    const buffer = new THREE.BufferGeometry().setFromPoints(points);
    const line = new THREE.Line(buffer, addressMat);
    chinaObj.add(line);
    return { points, curve }
}

function update() {
    // 每帧调用
    requestAnimationFrame(update);
    // 添加圆点动画
    animCircle.forEach(val => {
        val.circle.material.opacity = val.opacityparam * Math.pow(1 - val.offset, 2);
        val.circle.scale.set(
            val.scaleparam * val.offset,
            val.scaleparam * val.offset,
            val.scaleparam * val.offset
        )
        val.offset += val.speed
        val.offset %= 1
    })
    base.update();
    controls.update();
}

function resize() {
    base.resize();
}
</script>

<style lang="scss"></style>